package com.xugui.thumb.services.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xugui.thumb.constant.RedisLuaScriptConstant;
import com.xugui.thumb.mapper.ThumbMapper;
import com.xugui.thumb.pojo.dto.DoThumbRequest;
import com.xugui.thumb.pojo.entity.Thumb;
import com.xugui.thumb.pojo.entity.User;
import com.xugui.thumb.pojo.enums.LuaStatusEnum;
import com.xugui.thumb.services.ThumbService;
import com.xugui.thumb.services.UserService;
import com.xugui.thumb.utils.RedisKeyUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Arrays;

/**
 * Redis 实现点赞服务 (Redis 缓存 + 定时任务 + 补偿机制（凌晨自动检查）)  不需要加事务和锁
 * 临时点赞记录：按时间戳存储
 * (thumb:temp:{时间}) -> (userId: blogId, 操作类型（1点赞、-1取消点赞、0无变化）)
 *  点赞流程：
 *      1 检查用户是否已点赞（redis查询）
 *      2 未点赞则在Redis中记录点赞状态
 *      3 写入临时点赞记录，等待定时任务同步
 *      （1 2 3 一起执行）使用lua脚本保证操作原子性
 *  取消点赞流程：
 *      1 检查用户是否已点赞（redis查询）
 *      2 已点赞则在Redis中删除点赞状态
 *      3 写入临时取消点赞记录，等待定时任务同步
 *      （1 2 3 一起执行）使用lua脚本保证操作原子性
 *  数据同步机制：
 *      1 定时任务每10秒执行一次
 *      2 获取指定时间的临时记录
 *      3 批量处理点赞/取消点赞操作
 *      4 更新博客计数点赞
 *      5 删除已处理的临时记录
 *  补偿机制：
 *      1 每天凌晨2点执行补偿任务
 *      2 检查是否有未同步的临时记录
 *      3 对未同步的记录执行同步操作
 */
@Service("thumbServiceRedis")
@Slf4j
@RequiredArgsConstructor
public class ThumbServiceRedisImpl extends ServiceImpl<ThumbMapper, Thumb> implements ThumbService {
  
    private final UserService userService;
  
    private final RedisTemplate<String, Object> redisTemplate;

    @Override  
    public Boolean doThumb(DoThumbRequest doThumbRequest, HttpServletRequest request) {  
        if (doThumbRequest == null || doThumbRequest.getBlogId() == null) {  
            throw new RuntimeException("参数错误");  
        }  
        User loginUser = userService.getLoginUser(request);  // 用户 ID
        Long blogId = doThumbRequest.getBlogId();   // 博客 ID
  
        String timeSlice = getTimeSlice();  
        // Redis Key  
        String tempThumbKey = RedisKeyUtil.getTempThumbKey(timeSlice);  // 临时点赞记录  thumb:temp:时间戳
        String userThumbKey = RedisKeyUtil.getUserThumbKey(loginUser.getId());  // 用户点赞记录  thumb:用户ID
  
        // 执行 Lua 脚本
        // TODO 在 Lua 脚本中需要判断当前的 blogId 是否存在
        long result = redisTemplate.execute(  
                RedisLuaScriptConstant.THUMB_SCRIPT,
                Arrays.asList(tempThumbKey, userThumbKey),
                loginUser.getId(),    // 不需要转成字符串
                blogId  
        );  
  
        if (LuaStatusEnum.FAIL.getValue() == result) {  
            throw new RuntimeException("用户已点赞");  
        }  
  
        // 更新成功才执行  
        return LuaStatusEnum.SUCCESS.getValue() == result;
    }  
  
    @Override  
    public Boolean undoThumb(DoThumbRequest doThumbRequest, HttpServletRequest request) {
        if (doThumbRequest == null || doThumbRequest.getBlogId() == null) {  
            throw new RuntimeException("参数错误");  
        }  
        User loginUser = userService.getLoginUser(request);  
  
        Long blogId = doThumbRequest.getBlogId();  
        // 计算时间片  
        String timeSlice = getTimeSlice();  
        // Redis Key  
        String tempThumbKey = RedisKeyUtil.getTempThumbKey(timeSlice);  // 临时点赞记录  thumb:temp:时间戳
        String userThumbKey = RedisKeyUtil.getUserThumbKey(loginUser.getId());  // 用户点赞记录  thumb:用户ID
  
        // 执行 Lua 脚本  
        long result = redisTemplate.execute(  
                RedisLuaScriptConstant.UN_THUMB_SCRIPT,
                Arrays.asList(tempThumbKey, userThumbKey),  
                loginUser.getId(),   // 不需要转成字符串
                blogId  
        );  
        // 根据返回值处理结果  
        if (result == LuaStatusEnum.FAIL.getValue()) {  
            throw new RuntimeException("用户未点赞");  
        }  
        return LuaStatusEnum.SUCCESS.getValue() == result;  
    }

    /**
     * 获取时间片
     * @return 获取到当前时间前最近的整数秒
     */
    private String getTimeSlice() {
        DateTime nowDate = DateUtil.date();
        // 获取到当前时间前最近的整数秒 比如当前 11:20:23 获取到 11:20:20
        return DateUtil.format(nowDate, "HH:mm:") + (DateUtil.second(nowDate) / 10) * 10;  
    }

    @Override
    @Deprecated
    public Boolean hasThumb(Long blogId, Long userId) {  
        return redisTemplate.opsForHash().hasKey(RedisKeyUtil.getUserThumbKey(userId), blogId.toString());  
    }  
}
